package com.zhixing.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhixing.common.domain.entity.UserInfo;
import com.zhixing.common.domain.enums.Role;
import com.zhixing.common.exception.ZhixingException;
import com.zhixing.pay.domain.dto.TransactionDTO;
import com.zhixing.pay.domain.dto.TransactionQueryParams;
import com.zhixing.pay.domain.entity.TransactionRecord;
import com.zhixing.pay.domain.entity.Wallet;
import com.zhixing.pay.domain.entity.WalletInfo;
import com.zhixing.pay.domain.enums.TransactionType;
import com.zhixing.pay.domain.vo.TransactionRecordVO;
import com.zhixing.pay.domain.vo.WalletInfoVO;
import com.zhixing.pay.mapper.TransactionRecordMapper;
import com.zhixing.pay.mapper.WalletMapper;
import com.zhixing.pay.service.TransactionRecordService;
import com.zhixing.pay.service.WalletService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static com.zhixing.common.constant.Constant.*;
import static com.zhixing.common.domain.enums.HttpStatus.*;

@Service
@RequiredArgsConstructor
public class WalletServiceImpl implements WalletService {

    private final WalletMapper walletMapper;
    private final TransactionRecordService transactionRecordService;
    private final TransactionRecordMapper transactionRecordMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void initializeWallet(UserInfo userInfo) {
        // 初始化钱包
        Wallet wallet = Wallet.builder()
                .userId(userInfo.getId())
                .balance(new BigDecimal(userInfo.getRole() == Role.PASSENGER ? INIT_AMOUNT_PASSENGER : INIT_AMOUNT_DRIVER))
                .build();
        walletMapper.insert(wallet);

        // 乘客记录新人奖励
        if (userInfo.getRole() == Role.PASSENGER) {
            TransactionRecord transactionRecord = TransactionRecord.builder()
                    .walletId(wallet.getId())
                    .transactionType(TransactionType.INCOME)
                    .amount(wallet.getBalance())
                    .description(NEWCOMER_REWARD)
                    .transactionTime(LocalDateTime.now())
                    .build();
            transactionRecordMapper.insert(transactionRecord);
        }
    }

    @Override
    public WalletInfoVO getWalletInfo(Integer userId) {
        // 查询缓存
        WalletInfoVO walletInfoVO = new WalletInfoVO();
        WalletInfo walletInfo = (WalletInfo) redisTemplate.opsForValue().get(ZHIXING_WALLET_INFO + userId);
        if (ObjectUtil.isEmpty(walletInfo)) {
            // 查询钱包余额
            walletInfo = new WalletInfo();
            LambdaQueryWrapper<Wallet> lqw = new LambdaQueryWrapper<Wallet>()
                    .eq(Wallet::getUserId, userId);
            Wallet wallet = walletMapper.selectOne(lqw);
            walletInfo.setWalletId(wallet.getId());
            walletInfo.setBalance(wallet.getBalance());

            // 查询今日收支
            LocalDate now = LocalDate.now();
            QueryWrapper<TransactionRecord> qw = new QueryWrapper<TransactionRecord>()
                    .select("ifnull(sum(if(transaction_type = 0, amount, 0)), 0) todayIncome",
                            "ifnull(sum(if(transaction_type = 1, amount, 0)), 0) todayExpenditure")
                    .eq("wallet_id", wallet.getId())
                    .apply("date(transaction_time) = {0}", now);
            Map<String, Object> map = transactionRecordMapper.selectMaps(qw).get(0);
            walletInfo.setTodayIncome((BigDecimal) map.get("todayIncome"));
            walletInfo.setTodayExpenditure((BigDecimal) map.get("todayExpenditure"));

            // 存入缓存
            redisTemplate.opsForValue().set(ZHIXING_WALLET_INFO + userId, walletInfo, FIFTEEN_MINUTES, TimeUnit.MINUTES);
        }
        BeanUtil.copyProperties(walletInfo, walletInfoVO);

        // 查询交易记录
        TransactionQueryParams transactionQueryParams = new TransactionQueryParams();
        transactionQueryParams.setWalletId(Objects.requireNonNull(walletInfo).getWalletId());
        transactionQueryParams.setYearMonth(YearMonth.now());
        List<TransactionRecordVO> transactionRecordVO = transactionRecordService.getTransactionRecord(transactionQueryParams);
        walletInfoVO.setTransactionRecordVO(transactionRecordVO);
        return walletInfoVO;
    }

    @Override
    public void rechargeOrWithdrawal(Integer userId, TransactionDTO transactionDTO) {
        // 校验
        if (transactionDTO.getTransactionType() == TransactionType.INCOME
            && transactionDTO.getAmount().compareTo(new BigDecimal(RECHARGE_MAX)) > 0) {
            throw new ZhixingException(RECHARGE_AMOUNT_EXCEED);
        }
        LambdaQueryWrapper<Wallet> lqw = new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, userId);
        Wallet wallet = walletMapper.selectOne(lqw);
        if (transactionDTO.getTransactionType() == TransactionType.EXPENDITURE
            && transactionDTO.getAmount().compareTo(wallet.getBalance()) > 0) {
            throw new ZhixingException(WITHDRAWAL_AMOUNT_EXCEED);
        }

        // 更新钱包余额
        if (transactionDTO.getTransactionType() == TransactionType.INCOME) wallet.setBalance(wallet.getBalance().add(transactionDTO.getAmount()));
        else wallet.setBalance(wallet.getBalance().subtract(transactionDTO.getAmount()));
        walletMapper.updateById(wallet);

        // 记录交易，删除缓存
        TransactionRecord transactionRecord = TransactionRecord.builder()
                .walletId(wallet.getId())
                .description(transactionDTO.getTransactionType() == TransactionType.INCOME ? RECHARGE : WITHDRAWAL)
                .transactionTime(LocalDateTime.now())
                .build();
        BeanUtil.copyProperties(transactionDTO, transactionRecord);
        transactionRecordMapper.insert(transactionRecord);
        redisTemplate.delete(List.of(ZHIXING_WALLET_INFO + userId, ZHIXING_TRANSACTION_RECORD + wallet.getId() + ":" + YearMonth.now()));
    }

    @Override
    public void payOrder(BigDecimal realPayment, Integer userId) {
        // 查询余额是否充足
        LambdaQueryWrapper<Wallet> lqw = new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, userId);
        Wallet wallet = walletMapper.selectOne(lqw);
        if (ObjectUtil.isEmpty(wallet)) throw new ZhixingException(WALLET_NOT_EXIST);
        if (wallet.getBalance().compareTo(realPayment) < 0) throw new ZhixingException(BALANCE_NOT_SUFFICIENT);

        // 支付并记录交易，删除缓存
        wallet.setBalance(wallet.getBalance().subtract(realPayment));
        walletMapper.updateById(wallet);
        transactionRecordMapper.insert(new TransactionRecord(null, wallet.getId(),
                TransactionType.EXPENDITURE, realPayment, ORDER_PAYMENT, LocalDateTime.now()));
        redisTemplate.delete(List.of(ZHIXING_WALLET_INFO + userId, ZHIXING_TRANSACTION_RECORD + wallet.getId() + ":" + YearMonth.now()));
    }

    @Override
    public void driverRemit(BigDecimal realIncome, Integer userId) {
        // 查询钱包
        LambdaQueryWrapper<Wallet> lqw = new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, userId);
        Wallet wallet = walletMapper.selectOne(lqw);
        if (ObjectUtil.isEmpty(wallet)) throw new ZhixingException(WALLET_NOT_EXIST);

        // 打款并记录交易，删除缓存
        wallet.setBalance(wallet.getBalance().add(realIncome));
        walletMapper.updateById(wallet);
        transactionRecordMapper.insert(new TransactionRecord(null, wallet.getId(),
                TransactionType.INCOME, realIncome, ORDER_INCOME, LocalDateTime.now()));
        redisTemplate.delete(List.of(ZHIXING_WALLET_INFO + userId, ZHIXING_TRANSACTION_RECORD + wallet.getId() + ":" + YearMonth.now()));
    }
}
